Movement and Animation Headmovement and Flashlight Interactives Ragdoll Crouching and Hiding Walljump SideStep Crawling Destrucible and attack AgentMovementScript

Movement and Animation

Summary
This is mainly the code for the AgentMovement. I've created this with the new Unity Built in input system. This system comes in handy for me since this can be used for upgrades to controller instead of keyboard and mouse.

I am going to update this code later on in the development since it's a pretty large script and a lot can be put in a different script. When I do i'm going to update the code you see on the right to a scriptable object that stores all settings for the players movement.

Movement
What this code does is whenever you as the player starts to move it doesn't immediatly goes forward, but it goes forward smoothly with a velocity calculated on the bottom of this codebox (CalculateSpeed())

Also the Sprint() function is added to this movement. This is added with an fov effect to the camera for the player to really have the experience to run faster. And not to forget stamina, stamina is added to this movement so you can't fully sprint the whole game. Whenever you run out of stamina the Sprint mechanic is unable to be used and ofcourse when you stop sprinting it recharges, but recharges faster when standing still.

Forward Rotation
Because this is third person movement and not first person the rotation of the camera needs to be calculated to get the players forward rotation. First person controller have the ability to just rotate the player to the point the player wants to go to by just rotating the full body, but in the third person controller you want the playerbody to rotate freely for a visible rotation for the player to know where he is going/facing. How I completed this u can find in MoveAgent() is by calculating the cameras forward position by using the movemenInput. I first calculate the cameras forward with vector3 and then the right of the camera, I normalize these 2 vectors and calculate these 2 values with my movementInput z axis and x axis. I create this new vector3 cameraRelativeMovement and put both the forward as the right values inside.
I already created a protected vector3 for my main movementDirection and put the cameraRelativeMovement inside to create the new forward rotation. and last I call the CalculateSpeed() to apply the movement to the new forward rotation.

Crouch
The Crouch() is made smoothly with lerps and helps the developer with debugging for the perfect speed of crouching. What I mean with this is usually when you create a crouch mechanic you instantly downscale the collider on the player to make him crouch quickly through something. But not when you have animations, because animation take time to progress you might go underneath an object faster then the animation, so it would just clip the players mesh through and object. That's why I added a lerp to the collider to make it go as fast as the animation. As extra just like the sprint mechanic I used the camera fov to give out the illusion of the player walking slower.

Gravity
The Gravity() function speaks for itself and applies gravity to the player whenever it's not grounded. I do this by having a GroundCheck() function that shoots down a raycast and checks the layermask of the object. Whenever the raycast doesn't reach the ground of certain distance it's not grounded.

Zoom
As an extra I created the Zoom() function to zoom in to the forward rotation of the camera. This can be great for the game since it's a horror game, you have a flashlight and sometimes you don't see things yourself from youur point of view, but when you can zoom in this helps.

using UnityEngine.UI;
using Cinemachine;
	
[RequireComponent(typeof(Rigidbody))]
public class AgentMovement : MonoBehaviour
{
	public static AgentMovement instance;

	protected Rigidbody rb;

	protected Vector3 movementDirection;

	[field : SerializeField] public bool movementOff { get; set; }

	[field: SerializeField]
	public float currentVelocity { get; private set; }
	
	[Header("Player Info")]
	public Transform playerBody;

	[SerializeField] private Animator playerAnim;
	[SerializeField] private Slider stamina;

	[Header("Player Settings")]
	public float MaxWalkSpeed = 2.5f;
	public float MaxCrouchSpeed = 2;
	public float MaxRunSpeed = 5f;
	public float JumpForce = 2.0f;
	public float gravity = 5.0f;

	[Space(10)]
	[Header("Crouch Settings")]
	[SerializeField] private float crouchOffTime = 1;
	private bool crouchUsable = true;

	[Space(30)]
	[Header("Cam Settings")]
	[SerializeField] private GameObject mainCamera;

	[Space(10)]
	[Header("Vcam Settings")]
	[SerializeField] private CinemachineFreeLook mainVcam;

	[Space(50)]
	[Header("GroundCheck Settings")]
	[SerializeField] private LayerMask ground;

	[Header("GroundCheck Info")]
	[SerializeField] private bool grounded = false;


	public float gravityValue = -9.81f;
	public Vector3 playerVelocity;
	public bool jumpPressed = false;

	private void Awake()
	{
		instance = this;  
	}

	private void Start()
	{
		rb = GetComponent();
		playerAnim = GetComponentInChildren();
	}

	private void Update()
	{
		if (movementOff == false)
		{
			FunctionAble();
		}
		Stamina();
		Gravity();
		GroundCheck();
	}

	private void FixedUpdate()
	{
		RotateAgent();

		rb.velocity = movementDirection.normalized * currentVelocity;
	}

	private void FunctionAble()
	{
		if (inCrouch == false && inSideStep == false && inPunch == false && inWallJump == false && inCrawl == false && currentVelocity > 0.1f)
		{
			Zoom();
			Sprint();
		}
		else if (currentVelocity == 0)
			inSprint = false;
	}

	private void DefaultMovementsOff()
	{
		inCrouch = false;
		inSprint = false;

		playerAnim.SetBool("Running", false);
		playerAnim.SetBool("Crouch", false);

		VcamSettings.instance.FOVValue = 50;
	}

	/// <summary>
	/// Does check if groundcheck
	/// </summary>
	private void GroundCheck()
	{
		RaycastHit hit;
		if (Physics.Raycast(new Vector3(transform.position.x, transform.position.y, transform.position.z), Vector3.down, out hit, 1, ground))
		{
			Debug.DrawLine(transform.position, hit.point, Color.green);
			grounded = true;
		}
		else
			grounded = false;
	}

	private void Zoom()
	{
		if (Input.GetMouseButtonDown(1))
		{
			Debug.Log("right");
			mainVcam.m_Lens.FieldOfView = 20;
		}
	}

	private void Stamina()
	{
		if (currentVelocity > 0 && currentVelocity <= 2.5f && inSprint == false)
			stamina.value = Mathf.MoveTowards(stamina.value, stamina.maxValue, 0.6f * Time.deltaTime);
		else if (currentVelocity == 0)
			stamina.value = Mathf.MoveTowards(stamina.value, stamina.maxValue, 3 * Time.deltaTime);
	}

	/// <summary>
	/// When using Shift u can sprint with Fov effect of cinemachine cam
	/// </summary>
	private void Sprint()
	{
		if (Input.GetKey(KeyCode.LeftShift))
		{
			stamina.value = Mathf.MoveTowards(stamina.value, 0, 3 * Time.deltaTime);
			if (stamina.value >= 0.3f)
			{
				VcamSettings.instance.FOVValue = 60;
				inSprint = true;
			}
			else
			{
				VcamSettings.instance.FOVValue = 50;
				inSprint = false;
			}

		}
		else if (Input.GetKeyUp(KeyCode.LeftShift))
		{
			inSprint = false;
			VcamSettings.instance.FOVValue = 50;
		}
		else if (inCrouch == true)
			VcamSettings.instance.FOVValue = 50;

		if (inSprint == false && stamina.value != stamina.maxValue)
			stamina.value = Mathf.MoveTowards(stamina.value, stamina.maxValue, 1 * Time.deltaTime);
	}

	/// <summary>
	/// Applies gravity when not grounded
	/// </summary>
	private void Gravity()
	{
		if (jumpPressed && grounded == false)
		{
			rb.AddForce(0, JumpForce, 0, ForceMode.VelocityChange);
			JumpForce -= 10 * Time.deltaTime;
		}

		if (grounded == false && jumpPressed == false)
		{
			rb.AddForce(0, gravity, 0, ForceMode.VelocityChange);
			gravity -= 10 * Time.deltaTime;
		}
	}

	public void MovementChange(Vector3 movementInput)
	{
		if (jumpPressed)
		{
			if (inSprint)
				MoveAgent(movementInput, 20, 25, MaxRunSpeed);
			else
				MoveAgent(movementInput, 0.1f, 0.1f, MaxWalkSpeed);
		}


		if (jumpPressed == false && inSprint == false)
			MoveAgent(movementInput, 20, 25, MaxWalkSpeed);
		else if (inSprint == true)
			MoveAgent(movementInput, 20, 25, MaxRunSpeed);

		if (inCrouch && inSprint == false)
			MoveAgent(movementInput, 20, 25, MaxCrouchSpeed);
	}

	private void MoveAgent(Vector3 movementInput, float _acceleration, float _deacceleration, float _maxSpeed)
	{
		if (movementOff == false)
		{
			Vector3 forward = Camera.main.transform.forward;
			Vector3 right = Camera.main.transform.right;
			forward.y = 0;
			right.y = 0;
			forward = forward.normalized;
			right = right.normalized;

			Vector3 forwardRelativeVerticalInput = movementInput.z * forward;
			Vector3 rightRelativeVerticalInput = movementInput.x * right;

			Vector3 cameraRelativeMovement = forwardRelativeVerticalInput + rightRelativeVerticalInput;

			if (movementInput.magnitude > 0)
				movementDirection = cameraRelativeMovement;

			currentVelocity = CalculateSpeed(movementInput, _acceleration, _deacceleration, _maxSpeed);
		}
		else
			currentVelocity = 0;
	}

	/// <summary>
	/// Rotate player to where ur walking to
	/// </summary>
	private void RotateAgent()
	{
		if (currentVelocity != 0)
		{
			Quaternion toRotation = Quaternion.LookRotation(movementDirection, Vector3.up);

			transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, 8 * Time.deltaTime);
		}
	}

	/// <summary>
	/// if pressing crouch button move player down and scale down movement speed and fov
	/// </summary>
	private void Crouch()
	{
		if (Input.GetKey(KeyCode.LeftControl) && crouchUsable == true)
		{
			VcamSettings.instance.FOVValue = 40;
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, -0.2f, 2.5f * Time.deltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);
			inSprint = false;
			inCrouch = true;
		}
		else
		{
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, 0.3f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 1, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);

			if (crouchUsable == false)
			{
				crouchOffTime -= Time.deltaTime;
				if (crouchOffTime <= 0)
				{
					crouchUsable = true;
					crouchOffTime = 0.4f;
				}
			}

			if (takingCover == false)
				inCrouch = false;
		}

		if (Input.GetKeyUp(KeyCode.LeftControl))
		{
			crouchUsable = false;
			VcamSettings.instance.FOVValue = 50;
			inCrouch = false;
		}
	}

	public void GoCrouch()
	{
		playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, 0, 0.5f * Time.maximumDeltaTime), playerBody.transform.localPosition.z);
		playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);
	}

	private float CalculateSpeed(Vector3 movementInput, float _acceleration, float _deacceleration, float _maxSpeed)
	{
		if (movementInput.magnitude > 0)
			currentVelocity += _acceleration * Time.deltaTime;
		else
			currentVelocity -= _deacceleration * Time.deltaTime;

		return Mathf.Clamp(currentVelocity, 0, _maxSpeed);
	}   
}

Headmovement + Flashlight

Head Movement
What I created here with a little help from the internet is a head movement with vector4. Vector4 helps me saving 4 position in rotation. All I do here is rotate the head to the forward of the camera with smoothdamping but also clamping it with the vector4 values. After that I just added a light source to the BodyFollow variable and made it do the same forward rotation but without a clamp. This all together is a great addition to the game to give the person playing the game the feeling of really being the player.

public class HeadRotate : MonoBehaviour
{
[Tooltip("Set to any Transform, or null to look forward relative to the camera.")]
public Transform BodyFollow;

public Transform lookAt;
public float rotationSpeed = 0.25f;
public Vector4 limitLeftRightUpDown = new Vector4(-80, 80, -45, 45);
private float rxVel = 0;
private float ryVel = 0;

void Update()
	{
		if (BodyFollow != null)
		    transform.position = Vector3.Lerp(transform.position, BodyFollow.position, 2 * Time.maximumDeltaTime);

		Vector3 forward = (lookAt == null) ? Camera.main.transform.forward : (lookAt.position - transform.position).normalized;
		Vector3 prevRotation = transform.localRotation.eulerAngles;

		transform.forward = forward;
		Vector3 newRotation = transform.localRotation.eulerAngles;

		if (newRotation.x > 180) newRotation.x -= 360;
		if (newRotation.y > 180) newRotation.y -= 360;

		newRotation.x = Mathf.Clamp(newRotation.x, limitLeftRightUpDown.z, limitLeftRightUpDown.w);
		newRotation.y = Mathf.Clamp(newRotation.y, limitLeftRightUpDown.x, limitLeftRightUpDown.y);
		transform.localRotation = Quaternion.Euler(new Vector3(Mathf.SmoothDampAngle(prevRotation.x, newRotation.x, ref rxVel, rotationSpeed), Mathf.SmoothDampAngle(prevRotation.y, newRotation.y, ref ryVel, rotationSpeed)));
	}	
}

Interactives

Summary
This is the code provided in the AgentMovement script. What this code does is use a raycast to interact with certain layers(pickups). The raycast I use for this is the raycast I use for every mechanic you need to interact with.

Pickup
The Pickup() function creates a raycast from the camera forward rotation and shoots it to a certain distance. Whenever the object with that layer gets hit it enters the object script to change it colors
(variable outline.SetActive(true))
and gets all data needed from the pickup if I ever want to add anything like text or a weapon.

I am going to change the color instead of entering the object material to adding a shader that makes the object lit.

When the player is looking at the object and presses the keycode it could enter the object data with the tools provided if I add something.

The same thing for the doors, but instead of picking it up I open it with rotation lerps inside the ClosetDoors script.

public class AgentMovement : MonoBehaviour
{
	[Space(20)]
	[Header("Interactable Settings")]
	[SerializeField] private LayerMask pickups;
	[SerializeField] private LayerMask interactiveLayer;
	[SerializeField] private Pickup pickupItem; 
	[SerializeField] private ClosetDoors door;

	private void Update()
	{
		if (movementOff == false)
		{
			Doors();
			Pickup();
		}
	}

	private void Doors()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, interactiveLayer))
		{
			if (door != null)
			{
				if (door != hit.transform.GetComponent())
				{
					door.OnObject = false;
					door = null;
				}
			}
			else
			{
				door = hit.transform.GetComponent();

				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
					door.OnTheObject();
			}

			Debug.DrawLine(transform.position, hit.point, Color.red);
		}
		else
		{
			if (door != null)
			{
				door.OnObject = false;
				door = null;
			}
		}
	}    
	
	private void Pickup()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, pickups))
		{
			pickupItem = hit.transform.GetComponent();
			pickupItem.outline.SetActive(true);

			Debug.Log("Pickup");
			Debug.DrawLine(transform.position, hit.point, Color.red);


			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
				{
					print("picked up");
				}
			}
		}
		else
		{
			if (pickupItem != null)
			{
				pickupItem.outline.SetActive(false);
				pickupItem = null;
			}
		}
	}
}

Ragdoll (OnDeath)

Ragdoll
I created a script for the player to also be able to die. I achieved this by using the Unity ragdoll system where u can put in every limb of a character. Then all there is to do is turn of the main collider and rigibody and turn on every limbs own collider and rigibody. Ofcourse right now it's just for testing but later on I can easily add transition, text and all other things you need to respawn.

public class Kill : MonoBehaviour
{
	public CapsuleCollider MainCollider;
	public GameObject Rig;
	public Animator Animator;

	void Start()
	{
		Rig = this.gameObject;
		Animator = GetComponent();
		GetRagdollBits();
		RagdollModeOff();
	}

	void Update()
	{
		GetRagdollBits();


		if (Input.GetKeyDown(KeyCode.X))
			RagdollModeOn();


		if (Input.GetKeyDown(KeyCode.Y))
			RagdollModeOff();
	}

	public Collider[] ragDollColliders;
	public Rigidbody[] limbsRigibodies;
	private void GetRagdollBits()
	{
		ragDollColliders = Rig.GetComponentsInChildren();
		limbsRigibodies = Rig.GetComponentsInChildren();
	}

	private void RagdollModeOn()
	{
		GetComponentInParent().movementOff = true;

		Animator.enabled = false;
		MainCollider.enabled = false;

		foreach (Collider col in ragDollColliders)
		{
			col.enabled = true;
		}

		foreach (Rigidbody rigid in limbsRigibodies)
		{
			rigid.isKinematic = false;
		}

		GetComponentInParent().isKinematic = true;
	}
	
	private void RagdollModeOff()
	{
		GetComponentInParent().movementOff = false;

		Animator.enabled = true;

		foreach (Collider col in ragDollColliders)
		{
			col.enabled = false;
		}

		foreach (Rigidbody rigid in limbsRigibodies)
		{
			rigid.isKinematic = true;
		}

		MainCollider.enabled = true;
		GetComponentInParent().isKinematic = false;
	}
}

Crouching and Hiding

Summary
Since i've already explained a bit about the crouching and the raycast I use for my mechanics I will talk more about the hiding mechanic itself.

Cover
The TakeCover() function brings the addition to the game to hide from enemies or lines of fire. In some famous games like GTA 5 you as the player have the ability to hide behind walls by pressin a button. I created something simular but to certain layers of walls like you see in the video. Whenever your raycast is pointed at the wall a tooltip that follows your mouse position on the x-axis but stays the same on the y-axis. This helps the player to know you can hide behind that wall.

When you press the right button it automatically walks towards that exact point u pressed towards. This turns off your movement untill you reach the wall. It doesn't fully turns off your movement, because if you press a movement key while walking towards the wall it stop and you can continue walking yourself. the TakeCover() function mainly works on the raycast and the MoveToCover() does all the movement and calculations.

Another thing I added to the cover layered objects is an ontrigger, so if you've reached the wall and try moving yourself close to the wall it stays crouched untill you walk away from the wall. This helps the player progress it's journey and not being able to stand up immediatly and being caught by any enemy or line of fire.

This is still a bit buggy since when the player hugs the wall it tends to stay stuck to the wall but still play the animation of crouch walking. So ofcourse this is a fix on my to do list.

using UnityEngine.UI;
using Cinemachine;
	
[RequireComponent(typeof(Rigidbody))]
public class AgentMovement : MonoBehaviour
{
	[Header("Player Settings")]
	public float MaxCrouchSpeed = 2;

	[Space(10)]
	[Header("Crouch Settings")]
	[SerializeField] private float crouchOffTime = 1;
	private bool crouchUsable = true;

	[field: Space(20)]
	[Header("Hide Settings")]
	[SerializeField] private LayerMask coverLayer;

	[field: Space(20)]
	[Header("Hide Info")]
	[SerializeField] private HideWall hideWall;
	[SerializeField] private Transform hideTooltip;
	[SerializeField] private Vector3 coverPos;

	[field : Space(20)]
	[field : SerializeField] public bool inCrouch { get; private set; }

	public bool takingCover = false;


	private void Start()
	{
		playerAnim = GetComponentInChildren();
	}

	private void Update()
	{
		if (movementOff == false)
		{
			FunctionAble();
		}

		MoveToCover();
	}

	private void FunctionAble()
	{
		if (inPunch == false)
		{
			TakeCover();
		}

		if (inSideStep == false && inPunch == false && inWallJump == false && inCrawl == false)
			Crouch();
	}

	private void DefaultMovementsOff()
	{
		inCrouch = false;

		playerAnim.SetBool("Crouch", false);
	}
			
	/// <summary>
	/// Get wall Position where you can hide
	/// </summary>
	private void TakeCover()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 15, coverLayer))
		{
			if (Vector3.Distance(hit.transform.position, transform.position) >= 3)
			{
				Debug.DrawLine(transform.position, hit.point, Color.red);

				if (Vector3.Distance(hit.transform.position, transform.position) <= 10)
				{
					hideTooltip.gameObject.SetActive(true);
					hideTooltip.position = new Vector3(hit.point.x, hit.transform.position.y + 1.75f, hit.point.z);
				}
				else
					hideTooltip.gameObject.SetActive(false);

				if (Input.GetKeyDown(KeyCode.E))
				{
					if (Vector3.Distance(hit.transform.position, transform.position) <= 10)
					{
						inSprint = false;
						movementOff = true;
						currentVelocity = 0;
						hideWall = hit.transform.GetComponentInChildren();
						takingCover = true;
						playerAnim.CrossFade("TakeCover", 0.1f);
						inCrouch = true;
						coverPos = hit.point;
					}
				}
			}
		}
	}

	/// <summary>
	/// Moves to position TakeCover(); got
	/// </summary>
	private void MoveToCover() 
	{
		if (coverPos != new Vector3(0, 0, 0))
		{
			Vector3 lookPos = coverPos - transform.position;
			lookPos.y = 0;
			Quaternion coverRot = Quaternion.LookRotation(lookPos);

			transform.rotation = Quaternion.RotateTowards(transform.rotation, coverRot, 10 * Time.maximumDeltaTime);

			transform.position = Vector3.MoveTowards(transform.position, new Vector3(coverPos.x, transform.position.y, coverPos.z), 3 * Time.deltaTime);

			if (Vector3.Distance(transform.position, coverPos) <= 1)
			{
				movementOff = false;
				playerAnim.CrossFade("Crouch Idle", 0.5f);
				coverPos = new Vector3(0, 0, 0);
			}

			if (currentVelocity > 0)
			{
				inCrouch = false;
				coverPos = new Vector3(0, 0, 0);
			}
		}

		if (takingCover && currentVelocity > 0 && hideWall.hiding == false)
		{
			takingCover = false;
		}
	}

	public void MovementChange(Vector3 movementInput)
	{
		if (inCrouch && inSprint == false)
			MoveAgent(movementInput, 20, 25, MaxCrouchSpeed);
	}

	/// <summary>
	/// if pressing crouch button move player down and scale down movement speed and fov
	/// </summary>
	private void Crouch()
	{
		if (Input.GetKey(KeyCode.LeftControl) && crouchUsable == true)
		{
			VcamSettings.instance.FOVValue = 40;
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, -0.2f, 2.5f * Time.deltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);
			inSprint = false;
			inCrouch = true;
		}
		else
		{
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, 0.3f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 1, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);

			if (crouchUsable == false)
			{
				crouchOffTime -= Time.deltaTime;
				if (crouchOffTime <= 0)
				{
					crouchUsable = true;
					crouchOffTime = 0.4f;
				}
			}

			if (takingCover == false)
				inCrouch = false;
		}

		if (Input.GetKeyUp(KeyCode.LeftControl))
		{
			crouchUsable = false;
			VcamSettings.instance.FOVValue = 50;
			inCrouch = false;
		}
	}

	public void GoCrouch()
	{
		playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, 0, 0.5f * Time.maximumDeltaTime), playerBody.transform.localPosition.z);
		playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);
	}
}

Walljump

Summary
The wall jump mechanic is a great addition to the game because it's a fun way to progress to parts in a level, such as something that's blocking the path.

Wall Jump
I created this mechanic with the Walljump() function that does all the raycast and position information. And the JumpOverWall() function that does all the calculation, position and animations.

The raycast again finds the layer of the object needed to use this mechanic. When it hits the tooltip shows up to know how to use this mechanic. When you press the correct button the player starts to do it's "jump over" animation and the wallJumpCam is also turned on to get a cool cinematic transition over the wall.

The cool thing about UnityEngines Cinemachine is that you can add transitions if you turn off one of their camera. This helps a lot since you can create multiple individual cinemachine camera with their own fov, animation, rotation and following targets. Cinemachine then helps u with it's own smooth or hard transition of any changes on the cam itself such as the fov or rotation.



Wall Side
What I then created for the wall object itself is a way to decide which side of the wall you are on and which side you have to jump to whenever you press the correct button. This works great since I only call the wall objects script once when pressin the correct button.

using UnityEngine.UI;
using Cinemachine;
	
[RequireComponent(typeof(Rigidbody))]
public class AgentMovement : MonoBehaviour
{
	[field : SerializeField] public bool movementOff { get; set; }
	
	[Header("Player Info")]
	public Transform playerBody;

	[SerializeField] private Animator playerAnim;

	[Space(30)]
	[Header("Cam Settings")]
	[SerializeField] private GameObject mainCamera;
	[SerializeField] private GameObject wallJumpCam;

	[Space(10)]
	[Header("Vcam Settings")]
	[SerializeField] private CinemachineFreeLook mainVcam;
	[SerializeField] private CinemachineVirtualCamera sideStepVcam;

	[field: Space(20)]
	[Header("Jump Settings")]
	[SerializeField] private LayerMask jumpLayer;

	[field: Space(20)]
	[Header("Jump Info")]
	[SerializeField] private Vector3 jumpPos;

	[field: Space(20)]
	[Header("Hide Info")]
	[SerializeField] private Transform hideTooltip;

	[field : Space(20)]
	[field : SerializeField] public bool inWallJump { get; private set; }
	

	private void Update()
	{
		if (movementOff == false)
		{
			FunctionAble();
		}

		JumpOverWall();
	}

	private void FunctionAble()
	{
		if (inPunch == false)
		{
			WallJump();
		}
	}

	private void WallJump()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, jumpLayer))
		{
			Debug.DrawLine(transform.position, hit.point, Color.red);

			if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
			{
				hideTooltip.gameObject.SetActive(true);
				hideTooltip.position = new Vector3(hit.point.x, hit.transform.position.y + 1.75f, hit.point.z);
			}
			else
				hideTooltip.gameObject.SetActive(false);

			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
				{
					if (hit.transform.gameObject.GetComponent().Front == true)
					{
						jumpPos = new Vector3(transform.position.x, transform.position.y, hit.transform.position.z - 2);
						transform.rotation = Quaternion.LookRotation(-Vector3.forward, Vector3.up);
						hit.transform.gameObject.GetComponent().Front = false;
					}
					else if (hit.transform.gameObject.GetComponent().Back == true)
					{
						jumpPos = new Vector3(transform.position.x, transform.position.y, hit.transform.position.z + 2);
						transform.rotation = Quaternion.LookRotation(Vector3.forward, Vector3.up);
						hit.transform.gameObject.GetComponent().Back = false;
					}

					inWallJump = true;
					wallJumpCam.SetActive(true);
					movementOff = true;
					GetComponentInChildren().enabled = false;
					playerAnim.Play("Jump Over");
				}
			}
		}
	}

	private void JumpOverWall()
	{
		if (jumpPos != new Vector3(0, 0, 0))
		{
			transform.position = Vector3.Lerp(transform.position, new Vector3(jumpPos.x, transform.position.y, jumpPos.z), 2 * Time.deltaTime);

			if (Vector3.Distance(transform.position, jumpPos) <= 0.6f)
			{
				inWallJump = false;
				wallJumpCam.SetActive(false);
				movementOff = false;
				GetComponentInChildren().enabled = true;
				jumpPos = new Vector3(0, 0, 0);
			}
		}
	}
}
public class WallJump : MonoBehaviour
{
	public bool Front = false;
	public bool Back = false;
}
public class WalljumpSide : MonoBehaviour
{	

    [SerializeField] private bool front = false; 
    [SerializeField] private bool back = false; 

	private void OnTriggerStay(Collider other)
	{
		if (other.gameObject.CompareTag("Player"))
		{
			if (front == true)
				GetComponentInParent<WallJump>().Front = true;

			if (back == true)
				GetComponentInParent<WallJump>().Back = true;
		}
	}

	private void OnTriggerExit(Collider other)
	{
		if (front == true)
			GetComponentInParent<().Front = false;

		if (back == true)
			GetComponentInParent<().Back = false;
	}
}

SideStep

Summary
In lots of tripple AAA third person games such as A Way Out you have the ability to sidestep across tight ledges or caves. I recreated this mechanic and made it in my own style,

This AgentMovement mechanic is the Sidestep() mechanic this works the same as the other mechanics by first getting the information with the raycast and then calculating the rotations and movements.

Side Step
First the SideStep() function get all information on the object and calls it to use the SideStepping() function. The SideStepping() function is all about updating the position, rotation and distance. When u enter the sidestep mechanic I created the calculation to point the players forwards position to the Wall.EndPos.position. What I do then is turn off the default game movement and turn on the sidestep movement.

I am going to change the local movement to the new unity input system, but for now with debugging it works out fine.

I also turn on the sidestepping animation and cinemachine sidestep camera.

using Cinemachine;
	
[RequireComponent(typeof(Rigidbody))]
public class AgentMovement : MonoBehaviour
{
	[field : SerializeField] public bool movementOff { get; set; }

	[Header("Player Settings")]
	public float MaxSideStepSpeed = 2;

	[Space(30)]
	[Header("Cam Settings")]
	[SerializeField] private GameObject mainCamera;
	[SerializeField] private GameObject sideStepCam;

	[Space(10)]
	[Header("Vcam Settings")]
	[SerializeField] private CinemachineFreeLook mainVcam;
	[SerializeField] private CinemachineVirtualCamera sideStepVcam;

	[Space(20)]
	[Header("SideStep Settings")]
	[SerializeField] private LayerMask sideStepLayer;

	[field: Space(20)]
	[Header("SideStep Info")]
	public bool OnSideStepStart = false;

	[SerializeField] private WallWalk wall;

	private Quaternion sideStepRot;

	[field : Space(20)]
	[field : SerializeField] public bool inSideStep { get; private set; }
	[field: Space(20)]

	private void Start()
	{
		playerAnim = GetComponentInChildren();
	}

	private void Update()
	{
		if (movementOff == false)
		{
			SideStep(); 
		}

		SideStepping();
	}

	private void SideStep()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, sideStepLayer))
		{
			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 10)
				{
					DefaultMovementsOff();
					inSideStep = true;
					movementOff = true;
					sideStepCam.SetActive(true);
					wall = hit.transform.gameObject.GetComponentInParent();
					GetComponentInChildren().radius = 0.4f;
					playerAnim.Play("Left Turn");
				}
			}
		}
	}

	private void SideStepping()
	{
		if (inSideStep && OnSideStepStart)
		{
			if (Input.GetKey(KeyCode.W))
				transform.Translate(Vector3.forward * MaxSideStepSpeed * Time.deltaTime);

			if (Input.GetKeyDown(KeyCode.W))
				playerAnim.speed = 1;
			else if (Input.GetKeyUp(KeyCode.W))
				playerAnim.speed = 0.02f;
		}
		if (inSideStep)
		{
			if (transform.rotation != sideStepRot)
			{
				Vector3 lookPos = wall.EndPos.position - transform.position;
				lookPos.y = 0;
				sideStepRot = Quaternion.LookRotation(lookPos);

				transform.rotation = Quaternion.RotateTowards(transform.rotation, sideStepRot, 10 * Time.maximumDeltaTime);
			}

			if (Vector3.Distance(transform.position, new Vector3(wall.StartPos.position.x, wall.StartPos.position.y, wall.StartPos.position.z)) > 0.1f && OnSideStepStart == false)
				transform.position = Vector3.MoveTowards(transform.position, new Vector3(wall.StartPos.position.x, wall.StartPos.position.y, wall.StartPos.position.z), 2 * Time.deltaTime);

			if (Vector3.Distance(transform.position, new Vector3(wall.StartPos.position.x, wall.StartPos.position.y, wall.StartPos.position.z)) <= 0.1f)
				OnSideStepStart = true;

			if (Vector3.Distance(transform.position, wall.EndPos.transform.position) <= 0.5f)
			{
				GetComponentInChildren().radius = 0.5f;
				playerAnim.Play("Right Turn");
				mainCamera.transform.position = sideStepCam.transform.position;
				sideStepCam.SetActive(false);
				StartCoroutine(OutSideStep());
				OnSideStepStart = false;
				wall = null;
				inSideStep = false;
			}
		}
	}
	
	private IEnumerator OutSideStep()
	{
		yield return new WaitForSeconds(1);
		movementOff = false;
	}    
}

Crawling

Summary
Just like I told in the sidestep mechanic this is also something you see a lot in triple AAA third person games. Crawling through tight spaces is something I really wanted aswell for the variety of progression I can create in the level design.

Crawling
The Crawl() function again works for the raycast and gathering of all information that is needed for this mechanic. Then the Crawling() does all the updates for position, rotation, animation and settings. Again I've created the calculation to rotate the players forward rotation to the end point of the crawl object you've entered. I enter the crawl script and gather the Startpos and EndPos position to set the player right.

I turn off the defaultmovement en turn on the local movement for the crawling mechanic. Again I am going to change this from local to the new unity engine input system.

As extra I turn on the crawl virtual camera from cinemachine and set the camera to the players third person perspective. At the end when it reaches the EndPos of the Wall script it plays a little animation so the script has time to turn every default movement and setting on again. This helps a lot so you as player don't immediatly move and break the game by maybe walking back or something like that.

using Cinemachine;
	
[RequireComponent(typeof(Rigidbody))]
public class AgentMovement : MonoBehaviour
{
	protected Vector3 movementDirection;

	[field : SerializeField] public bool movementOff { get; set; }

	[Header("Player Settings")]
	public float MaxCrawlSpeed = 2;

	[Space(30)]
	[Header("Cam Settings")]
	[SerializeField] private GameObject mainCamera;
	[SerializeField] private GameObject crawlCam;

	[Space(10)]
	[Header("Vcam Settings")]
	[SerializeField] private CinemachineFreeLook mainVcam;
	
	[Space(60)]
	[Header("Crawl Settings")]
	[SerializeField] private LayerMask crawlLayer;

	[field: Space(20)]
	[Header("Crawl Info")]
	public bool onCrawlStart = false;

	[SerializeField] private Crawl crawl;
	private Quaternion crawlRot;

	[field : Space(20)]
	[field : SerializeField] public bool inCrawl { get; private set; }
	[field: Space(20)]


	private void Update()
	{
		if (movementOff == false)
		{
			Crawl();
		}

		Crawling();
	}

	private void Crawl()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 5, crawlLayer))
		{
			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
				{
					DefaultMovementsOff();
					inCrawl = true;
					movementOff = true;
					crawlCam.SetActive(true);
					crawl = hit.transform.gameObject.GetComponentInParent();
					playerAnim.Play("Laying Down");
				}
			}
		}
	}

	private void Crawling()
	{
		if (inCrawl && onCrawlStart)
		{
			if (Input.GetKey(KeyCode.W))
				transform.Translate(Vector3.forward * MaxCrawlSpeed * Time.deltaTime);

			if (Input.GetKeyDown(KeyCode.W))
				playerAnim.speed = 1;
			else if (Input.GetKeyUp(KeyCode.W))
				playerAnim.speed = 0.02f;
		}
		if (inCrawl)
		{
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, -0.2f, 2.5f * Time.deltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);

			if (transform.rotation != crawlRot)
			{
				Vector3 lookPos = crawl.EndPos.position - transform.position;
				lookPos.y = 0;
				crawlRot = Quaternion.LookRotation(lookPos);

				transform.rotation = Quaternion.RotateTowards(transform.rotation, crawlRot, 10 * Time.maximumDeltaTime);
			}

			if (Vector3.Distance(transform.position, new Vector3(crawl.StartPos.position.x, crawl.StartPos.position.y, crawl.StartPos.position.z)) > 0.1f && onCrawlStart == false)
				transform.position = Vector3.MoveTowards(transform.position, new Vector3(crawl.StartPos.position.x, crawl.StartPos.position.y, crawl.StartPos.position.z), 1 * Time.deltaTime);

			if (Vector3.Distance(transform.position, new Vector3(crawl.StartPos.position.x, crawl.StartPos.position.y, crawl.StartPos.position.z)) <= 0.1f)
				onCrawlStart = true;

			if (Vector3.Distance(transform.position, crawl.EndPos.transform.position) <= 0.5f)
			{
				GetComponentInChildren().radius = 0.5f;
				playerAnim.Play("Getting Up");
				mainCamera.transform.position = crawlCam.transform.position;
				crawlCam.SetActive(false);
				StartCoroutine(OutCrawl());
				onCrawlStart = false;
				crawl = null;
				inCrawl = false;
			}
		}
	}

	private IEnumerator OutCrawl()
	{
		yield return new WaitForSeconds(4.5f);
		movementOff = false;
	}
}

Destrucibles + Attack

Summary
This mechanic is something I added for fun, because in most can there is usually something you can destroy or move. So I've created the abilty to punch down walls. This is going to be change later on in the development if I might add something like a crowbar.

Destructible
Because the attack is pretty basic with an animation and collision I will talk more about the destructible. For now I've just created a wall with individual blocks that have no movement. On the players fist there is a collider that only turn on When the PunchCol() function is called. The function is public since I use an event in the animator that turns on the collider exactly at the moment the player animation does the punch, otherwise you would be able to hug the wall and it gets destroyed instantly because of the collision.

On the wall there is a script BreakableWall that stores all rigibodies and collisions. When the "PlayerFist" hits the walls collider a explosion force is added from the rigibodies with a random power between(50, 250). This creates some diversity in every breakable wall.

This already works greatly and I can add any kind of object to it with individual pieces, but there is going to be improvement later on in the development for better hit precision and force.

using UnityEngine.UI;
using Cinemachine;
	
[RequireComponent(typeof(Rigidbody))]
public class AgentMovement : MonoBehaviour
{
	[field : SerializeField] public bool movementOff { get; set; }

	[Space(20)]
	[Header("Punch Info")]
	public BoxCollider FistColl;

	[field : Space(20)]
	[field : SerializeField] public bool inPunch { get; private set; }
	[field: Space(20)]

	private void Update()
	{
		if (movementOff == false)
		{
			FunctionAble();
		}

	}

	private void FunctionAble()
	{
		if (inCrouch == false && inSideStep == false && inWallJump == false && inCrawl == false)
			Attack();
	}

	private void Attack()
	{
		if (Input.GetMouseButtonDown(0))
		{
			movementOff = true;
			inPunch = true;
			playerAnim.SetBool("InPunch", true);
		}
	}

	public void PunchCol()
	{
		FistColl.enabled = true;
	}

	public void OutPunch()
	{
		FistColl.enabled = false;
		movementOff = false;
		inPunch = false;
		playerAnim.SetBool("InPunch", false);
	}
}
public class BreakableWall : MonoBehaviour
{
	public List<Rigidbody> WallParts;

	private void OnTriggerEnter(Collider other)
	{
		if (other.gameObject.CompareTag("PlayerFist"))
		{
			for (int i = 0; i < WallParts.Count; i++)
			{
				WallParts[i].isKinematic = false;
				WallParts[i].AddExplosionForce(Random.Range(50, 250), Vector3.right, 50);
			}
		}
	}
}

Full AgentMovement Script

using UnityEngine.UI;
using Cinemachine;
	
[RequireComponent(typeof(Rigidbody))]
public class AgentMovement : MonoBehaviour
{
	public static AgentMovement instance;

	protected Rigidbody rb;

	protected Vector3 movementDirection;

	[field : SerializeField] public bool movementOff { get; set; }

	[field: SerializeField]
	public float currentVelocity { get; private set; }
	
	[Header("Player Info")]
	public Transform playerBody;

	[SerializeField] private Animator playerAnim;
	[SerializeField] private Slider stamina;

	[Header("Player Settings")]
	public float MaxWalkSpeed = 2.5f;
	public float MaxCrouchSpeed = 2;
	public float MaxRunSpeed = 5f;
	public float MaxSideStepSpeed = 2;
	public float MaxCrawlSpeed = 2;
	public float JumpForce = 2.0f;
	public float gravity = 5.0f;

	[Space(10)]
	[Header("Crouch Settings")]
	[SerializeField] private float crouchOffTime = 1;
	private bool crouchUsable = true;

	[Space(30)]
	[Header("Cam Settings")]
	[SerializeField] private GameObject mainCamera;
	[SerializeField] private GameObject wallJumpCam;
	[SerializeField] private GameObject sideStepCam;
	[SerializeField] private GameObject crawlCam;

	[Space(10)]
	[Header("Vcam Settings")]
	[SerializeField] private CinemachineFreeLook mainVcam;
	[SerializeField] private CinemachineVirtualCamera sideStepVcam;

	[Space(50)]
	[Header("GroundCheck Settings")]
	[SerializeField] private LayerMask ground;

	[Header("GroundCheck Info")]
	[SerializeField] private bool grounded = false;

	[Space(20)]
	[Header("Interactable Settings")]
	[SerializeField] private LayerMask pickups;
	[SerializeField] private LayerMask interactiveLayer;
	[SerializeField] private Pickup pickupItem; 
	[SerializeField] private ClosetDoors door;

	[Space(20)]
	[Header("Punch Info")]
	public BoxCollider FistColl;

	[Space(60)]
	[Header("Crawl Settings")]
	[SerializeField] private LayerMask crawlLayer;

	[field: Space(20)]
	[Header("Crawl Info")]
	public bool onCrawlStart = false;

	[SerializeField] private Crawl crawl;
	private Quaternion crawlRot;

	[Space(20)]
	[Header("SideStep Settings")]
	[SerializeField] private LayerMask sideStepLayer;

	[field: Space(20)]
	[Header("SideStep Info")]
	public bool OnSideStepStart = false;

	[SerializeField] private WallWalk wall;

	private Quaternion sideStepRot;

	[field: Space(20)]
	[Header("Jump Settings")]
	[SerializeField] private LayerMask jumpLayer;

	[field: Space(20)]
	[Header("Jump Info")]
	[SerializeField] private Vector3 jumpPos;

	[field: Space(20)]
	[Header("Hide Settings")]
	[SerializeField] private LayerMask coverLayer;

	[field: Space(20)]
	[Header("Hide Info")]
	[SerializeField] private HideWall hideWall;
	[SerializeField] private Transform hideTooltip;
	[SerializeField] private Vector3 coverPos;

	[field : Space(20)]
	[field : SerializeField] public bool inCrouch { get; private set; }
	[field : SerializeField] public bool inWallJump { get; private set; }
	[field : SerializeField] public bool inSprint { get; private set; }
	[field : SerializeField] public bool inSideStep { get; private set; }
	[field : SerializeField] public bool inCrawl { get; private set; }
	[field : SerializeField] public bool inPunch { get; private set; }
	[field: Space(20)]

	public bool takingCover = false;

	public float gravityValue = -9.81f;
	public Vector3 playerVelocity;
	public bool jumpPressed = false;

	private void Awake()
	{
		instance = this;  
	}

	private void Start()
	{
		rb = GetComponent();
		playerAnim = GetComponentInChildren();
	}

	private void Update()
	{
		if (movementOff == false)
		{
			FunctionAble();
			Doors();
			Pickup();

			OnJump();

			SideStep(); 
			Crawl();
		}

		Stamina();
		Crawling();
		SideStepping();
		MoveToCover();
		JumpOverWall();

		Gravity();
		GroundCheck();
	}

	private void FixedUpdate()
	{
		RotateAgent();

		rb.velocity = movementDirection.normalized * currentVelocity;
	}

	private void OnCollisionEnter()
	{
		jumpPressed = false;
		JumpForce = 2.5f;
		gravity = 0;
	}

	private void FunctionAble()
	{
		if (inPunch == false)
		{
			WallJump();
			TakeCover();
		}

		if (inSideStep == false && inPunch == false && inWallJump == false && inCrawl == false)
			Crouch();

		if (inCrouch == false && inSideStep == false && inPunch == false && inWallJump == false && inCrawl == false && currentVelocity > 0.1f)
		{
			Zoom();
			Sprint();
		}
		else if (currentVelocity == 0)
			inSprint = false;

		if (inCrouch == false && inSideStep == false && inWallJump == false && inCrawl == false)
			Attack();
	}

	private void DefaultMovementsOff()
	{
		inCrouch = false;
		inSprint = false;

		playerAnim.SetBool("Running", false);
		playerAnim.SetBool("Crouch", false);

		VcamSettings.instance.FOVValue = 50;
	}

	/// <summary>
	/// Does check if groundcheck
	/// </summary>
	private void GroundCheck()
	{
		RaycastHit hit;
		if (Physics.Raycast(new Vector3(transform.position.x, transform.position.y, transform.position.z), Vector3.down, out hit, 1, ground))
		{
			Debug.DrawLine(transform.position, hit.point, Color.green);
			grounded = true;
		}
		else
			grounded = false;
	}

	private void Zoom()
	{
		if (Input.GetMouseButtonDown(1))
		{
			Debug.Log("right");
			mainVcam.m_Lens.FieldOfView = 20;
		}
	}

	private void Attack()
	{
		if (Input.GetMouseButtonDown(0))
		{
			movementOff = true;
			inPunch = true;
			playerAnim.SetBool("InPunch", true);
		}
	}

	public void PunchCol()
	{
		FistColl.enabled = true;
	}

	public void OutPunch()
	{
		FistColl.enabled = false;
		movementOff = false;
		inPunch = false;
		playerAnim.SetBool("InPunch", false);
	}

	private void Stamina()
	{
		if (currentVelocity > 0 && currentVelocity <= 2.5f && inSprint == false)
			stamina.value = Mathf.MoveTowards(stamina.value, stamina.maxValue, 0.6f * Time.deltaTime);
		else if (currentVelocity == 0)
			stamina.value = Mathf.MoveTowards(stamina.value, stamina.maxValue, 3 * Time.deltaTime);
	}

	/// <summary>
	/// When using Shift u can sprint with Fov effect of cinemachine cam
	/// </summary>
	private void Sprint()
	{
		if (Input.GetKey(KeyCode.LeftShift))
		{
			stamina.value = Mathf.MoveTowards(stamina.value, 0, 3 * Time.deltaTime);
			if (stamina.value >= 0.3f)
			{
				VcamSettings.instance.FOVValue = 60;
				inSprint = true;
			}
			else
			{
				VcamSettings.instance.FOVValue = 50;
				inSprint = false;
			}

		}
		else if (Input.GetKeyUp(KeyCode.LeftShift))
		{
			inSprint = false;
			VcamSettings.instance.FOVValue = 50;
		}
		else if (inCrouch == true)
			VcamSettings.instance.FOVValue = 50;

		if (inSprint == false && stamina.value != stamina.maxValue)
			stamina.value = Mathf.MoveTowards(stamina.value, stamina.maxValue, 1 * Time.deltaTime);
	}

	private void SideStep()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, sideStepLayer))
		{
			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 10)
				{
					DefaultMovementsOff();
					inSideStep = true;
					movementOff = true;
					sideStepCam.SetActive(true);
					wall = hit.transform.gameObject.GetComponentInParent();
					GetComponentInChildren().radius = 0.4f;
					playerAnim.Play("Left Turn");
				}
			}
		}
	}

	private void SideStepping()
	{
		if (inSideStep && OnSideStepStart)
		{
			if (Input.GetKey(KeyCode.W))
				transform.Translate(Vector3.forward * MaxSideStepSpeed * Time.deltaTime);

			if (Input.GetKeyDown(KeyCode.W))
				playerAnim.speed = 1;
			else if (Input.GetKeyUp(KeyCode.W))
				playerAnim.speed = 0.02f;
		}
		if (inSideStep)
		{
			if (transform.rotation != sideStepRot)
			{
				Vector3 lookPos = wall.EndPos.position - transform.position;
				lookPos.y = 0;
				sideStepRot = Quaternion.LookRotation(lookPos);

				transform.rotation = Quaternion.RotateTowards(transform.rotation, sideStepRot, 10 * Time.maximumDeltaTime);
			}

			if (Vector3.Distance(transform.position, new Vector3(wall.StartPos.position.x, wall.StartPos.position.y, wall.StartPos.position.z)) > 0.1f && OnSideStepStart == false)
				transform.position = Vector3.MoveTowards(transform.position, new Vector3(wall.StartPos.position.x, wall.StartPos.position.y, wall.StartPos.position.z), 2 * Time.deltaTime);

			if (Vector3.Distance(transform.position, new Vector3(wall.StartPos.position.x, wall.StartPos.position.y, wall.StartPos.position.z)) <= 0.1f)
				OnSideStepStart = true;

			if (Vector3.Distance(transform.position, wall.EndPos.transform.position) <= 0.5f)
			{
				GetComponentInChildren().radius = 0.5f;
				playerAnim.Play("Right Turn");
				mainCamera.transform.position = sideStepCam.transform.position;
				sideStepCam.SetActive(false);
				StartCoroutine(OutSideStep());
				OnSideStepStart = false;
				wall = null;
				inSideStep = false;
			}
		}
	}

	private void Crawl()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 5, crawlLayer))
		{
			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
				{
					DefaultMovementsOff();
					inCrawl = true;
					movementOff = true;
					crawlCam.SetActive(true);
					crawl = hit.transform.gameObject.GetComponentInParent();
					playerAnim.Play("Laying Down");
				}
			}
		}
	}

	private void Crawling()
	{
		if (inCrawl && onCrawlStart)
		{
			if (Input.GetKey(KeyCode.W))
				transform.Translate(Vector3.forward * MaxCrawlSpeed * Time.deltaTime);

			if (Input.GetKeyDown(KeyCode.W))
				playerAnim.speed = 1;
			else if (Input.GetKeyUp(KeyCode.W))
				playerAnim.speed = 0.02f;
		}
		if (inCrawl)
		{
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, -0.2f, 2.5f * Time.deltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);

			if (transform.rotation != crawlRot)
			{
				Vector3 lookPos = crawl.EndPos.position - transform.position;
				lookPos.y = 0;
				crawlRot = Quaternion.LookRotation(lookPos);

				transform.rotation = Quaternion.RotateTowards(transform.rotation, crawlRot, 10 * Time.maximumDeltaTime);
			}

			if (Vector3.Distance(transform.position, new Vector3(crawl.StartPos.position.x, crawl.StartPos.position.y, crawl.StartPos.position.z)) > 0.1f && onCrawlStart == false)
				transform.position = Vector3.MoveTowards(transform.position, new Vector3(crawl.StartPos.position.x, crawl.StartPos.position.y, crawl.StartPos.position.z), 1 * Time.deltaTime);

			if (Vector3.Distance(transform.position, new Vector3(crawl.StartPos.position.x, crawl.StartPos.position.y, crawl.StartPos.position.z)) <= 0.1f)
				onCrawlStart = true;

			if (Vector3.Distance(transform.position, crawl.EndPos.transform.position) <= 0.5f)
			{
				GetComponentInChildren().radius = 0.5f;
				playerAnim.Play("Getting Up");
				mainCamera.transform.position = crawlCam.transform.position;
				crawlCam.SetActive(false);
				StartCoroutine(OutCrawl());
				onCrawlStart = false;
				crawl = null;
				inCrawl = false;
			}
		}
	}

	private void WallJump()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, jumpLayer))
		{
			Debug.DrawLine(transform.position, hit.point, Color.red);

			if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
			{
				hideTooltip.gameObject.SetActive(true);
				hideTooltip.position = new Vector3(hit.point.x, hit.transform.position.y + 1.75f, hit.point.z);
			}
			else
				hideTooltip.gameObject.SetActive(false);

			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
				{
					if (hit.transform.gameObject.GetComponent().Front == true)
					{
						jumpPos = new Vector3(transform.position.x, transform.position.y, hit.transform.position.z - 2);
						transform.rotation = Quaternion.LookRotation(-Vector3.forward, Vector3.up);
						hit.transform.gameObject.GetComponent().Front = false;
					}
					else if (hit.transform.gameObject.GetComponent().Back == true)
					{
						jumpPos = new Vector3(transform.position.x, transform.position.y, hit.transform.position.z + 2);
						transform.rotation = Quaternion.LookRotation(Vector3.forward, Vector3.up);
						hit.transform.gameObject.GetComponent().Back = false;
					}

					inWallJump = true;
					wallJumpCam.SetActive(true);
					movementOff = true;
					GetComponentInChildren().enabled = false;
					playerAnim.Play("Jump Over");
				}
			}
		}
	}

	private void JumpOverWall()
	{
		if (jumpPos != new Vector3(0, 0, 0))
		{
			transform.position = Vector3.Lerp(transform.position, new Vector3(jumpPos.x, transform.position.y, jumpPos.z), 2 * Time.deltaTime);

			if (Vector3.Distance(transform.position, jumpPos) <= 0.6f)
			{
				inWallJump = false;
				wallJumpCam.SetActive(false);
				movementOff = false;
				GetComponentInChildren().enabled = true;
				jumpPos = new Vector3(0, 0, 0);
			}
		}
	}
			
	/// <summary>
	/// Get wall Position where you can hide
	/// </summary>
	private void TakeCover()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 15, coverLayer))
		{
			if (Vector3.Distance(hit.transform.position, transform.position) >= 3)
			{
				Debug.DrawLine(transform.position, hit.point, Color.red);

				if (Vector3.Distance(hit.transform.position, transform.position) <= 10)
				{
					hideTooltip.gameObject.SetActive(true);
					hideTooltip.position = new Vector3(hit.point.x, hit.transform.position.y + 1.75f, hit.point.z);
				}
				else
					hideTooltip.gameObject.SetActive(false);

				if (Input.GetKeyDown(KeyCode.E))
				{
					if (Vector3.Distance(hit.transform.position, transform.position) <= 10)
					{
						inSprint = false;
						movementOff = true;
						currentVelocity = 0;
						hideWall = hit.transform.GetComponentInChildren();
						takingCover = true;
						playerAnim.CrossFade("TakeCover", 0.1f);
						inCrouch = true;
						coverPos = hit.point;
					}
				}
			}
		}
	}

	/// <summary>
	/// Moves to position TakeCover(); got
	/// </summary>
	private void MoveToCover() 
	{
		if (coverPos != new Vector3(0, 0, 0))
		{
			Vector3 lookPos = coverPos - transform.position;
			lookPos.y = 0;
			Quaternion coverRot = Quaternion.LookRotation(lookPos);

			transform.rotation = Quaternion.RotateTowards(transform.rotation, coverRot, 10 * Time.maximumDeltaTime);

			transform.position = Vector3.MoveTowards(transform.position, new Vector3(coverPos.x, transform.position.y, coverPos.z), 3 * Time.deltaTime);

			if (Vector3.Distance(transform.position, coverPos) <= 1)
			{
				movementOff = false;
				playerAnim.CrossFade("Crouch Idle", 0.5f);
				coverPos = new Vector3(0, 0, 0);
			}

			if (currentVelocity > 0)
			{
				inCrouch = false;
				coverPos = new Vector3(0, 0, 0);
			}
		}

		if (takingCover && currentVelocity > 0 && hideWall.hiding == false)
		{
			takingCover = false;
		}
	}

	/// <summary>
	/// Applies gravity when not grounded
	/// </summary>
	private void Gravity()
	{
		if (jumpPressed && grounded == false)
		{
			rb.AddForce(0, JumpForce, 0, ForceMode.VelocityChange);
			JumpForce -= 10 * Time.deltaTime;
		}

		if (grounded == false && jumpPressed == false)
		{
			rb.AddForce(0, gravity, 0, ForceMode.VelocityChange);
			gravity -= 10 * Time.deltaTime;
		}
	}

	/// <summary>
	/// when jumping u jump keeping the same velocity
	/// </summary>
	private void OnJump()
	{
		if (Input.GetKeyDown(KeyCode.Space) && grounded && jumpPressed == false)
		{
			rb.velocity = movementDirection.normalized * currentVelocity;
			jumpPressed = true;
			grounded = false;
		}
	}

	public void MovementChange(Vector3 movementInput)
	{
		if (jumpPressed)
		{
			if (inSprint)
				MoveAgent(movementInput, 20, 25, MaxRunSpeed);
			else
				MoveAgent(movementInput, 0.1f, 0.1f, MaxWalkSpeed);
		}


		if (jumpPressed == false && inSprint == false)
			MoveAgent(movementInput, 20, 25, MaxWalkSpeed);
		else if (inSprint == true)
			MoveAgent(movementInput, 20, 25, MaxRunSpeed);

		if (inCrouch && inSprint == false)
			MoveAgent(movementInput, 20, 25, MaxCrouchSpeed);
	}

	private void MoveAgent(Vector3 movementInput, float _acceleration, float _deacceleration, float _maxSpeed)
	{
		if (movementOff == false)
		{
			Vector3 forward = Camera.main.transform.forward;
			Vector3 right = Camera.main.transform.right;
			forward.y = 0;
			right.y = 0;
			forward = forward.normalized;
			right = right.normalized;

			Vector3 forwardRelativeVerticalInput = movementInput.z * forward;
			Vector3 rightRelativeVerticalInput = movementInput.x * right;

			Vector3 cameraRelativeMovement = forwardRelativeVerticalInput + rightRelativeVerticalInput;

			if (movementInput.magnitude > 0)
				movementDirection = cameraRelativeMovement;

			currentVelocity = CalculateSpeed(movementInput, _acceleration, _deacceleration, _maxSpeed);
		}
		else
			currentVelocity = 0;
	}

	/// <summary>
	/// Rotate player to where ur walking to
	/// </summary>
	private void RotateAgent()
	{
		if (currentVelocity != 0)
		{
			Quaternion toRotation = Quaternion.LookRotation(movementDirection, Vector3.up);

			transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, 8 * Time.deltaTime);
		}
	}

	/// <summary>
	/// if pressing crouch button move player down and scale down movement speed and fov
	/// </summary>
	private void Crouch()
	{
		if (Input.GetKey(KeyCode.LeftControl) && crouchUsable == true)
		{
			VcamSettings.instance.FOVValue = 40;
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, -0.2f, 2.5f * Time.deltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);
			inSprint = false;
			inCrouch = true;
		}
		else
		{
			playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, 0.3f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localPosition.z);
			playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 1, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);

			if (crouchUsable == false)
			{
				crouchOffTime -= Time.deltaTime;
				if (crouchOffTime <= 0)
				{
					crouchUsable = true;
					crouchOffTime = 0.4f;
				}
			}

			if (takingCover == false)
				inCrouch = false;
		}

		if (Input.GetKeyUp(KeyCode.LeftControl))
		{
			crouchUsable = false;
			VcamSettings.instance.FOVValue = 50;
			inCrouch = false;
		}
	}

	public void GoCrouch()
	{
		playerBody.transform.localPosition = new Vector3(playerBody.transform.localPosition.x, Mathf.MoveTowards(playerBody.transform.localPosition.y, 0, 0.5f * Time.maximumDeltaTime), playerBody.transform.localPosition.z);
		playerBody.transform.localScale = new Vector3(playerBody.transform.localScale.x, Mathf.Lerp(playerBody.transform.localScale.y, 0.5f, 0.5f * Time.maximumDeltaTime), playerBody.transform.localScale.z);
	}

	private void Doors()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, interactiveLayer))
		{
			if (door != null)
			{
				if (door != hit.transform.GetComponent())
				{
					door.OnObject = false;
					door = null;
				}
			}
			else
			{
				door = hit.transform.GetComponent();

				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
					door.OnTheObject();
			}

			Debug.DrawLine(transform.position, hit.point, Color.red);
		}
		else
		{
			if (door != null)
			{
				door.OnObject = false;
				door = null;
			}
		}
	}    
	
	private void Pickup()
	{
		RaycastHit hit;
		if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, 10, pickups))
		{
			pickupItem = hit.transform.GetComponent();
			pickupItem.outline.SetActive(true);

			Debug.Log("Pickup");
			Debug.DrawLine(transform.position, hit.point, Color.red);


			if (Input.GetKeyDown(KeyCode.E))
			{
				if (Vector3.Distance(hit.transform.position, transform.position) <= 5)
				{
					print("picked up");
				}
			}
		}
		else
		{
			if (pickupItem != null)
			{
				pickupItem.outline.SetActive(false);
				pickupItem = null;
			}
		}
	}

	private float CalculateSpeed(Vector3 movementInput, float _acceleration, float _deacceleration, float _maxSpeed)
	{
		if (movementInput.magnitude > 0)
			currentVelocity += _acceleration * Time.deltaTime;
		else
			currentVelocity -= _deacceleration * Time.deltaTime;

		return Mathf.Clamp(currentVelocity, 0, _maxSpeed);
	}   
	
	private IEnumerator OutSideStep()
	{
		yield return new WaitForSeconds(1);
		movementOff = false;
	}    

	private IEnumerator OutCrawl()
	{
		yield return new WaitForSeconds(4.5f);
		movementOff = false;
	}
}